//
// @Author: Zhenwei Xiao
// @Description:
// @Version: 1.0.0
// @Date: 2021/3/29 3:19 下午
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
//

const com = require("../minsecurity/common")
const persist = require("../minsecurity/identity/persist/persist")
const identity = require("../minsecurity/identity/identity")
const sqlite = require("../minsecurity/identity/persist/sqlite/sqlite")
const util = require('util')
const sm2 = require("../minsecurity/crypto/sm2/external")
const KeyChain = require("./KeyChain")
const sec = require("../minsecurity/crypto")

class IdentifyManager {
    constructor(identifies, defaultIdentity, privateKeyEncryptionAlgorithm){
        // 一个map，存储了身份名字和网络身份实体的映射
        this.identifies = identifies
        // 默认网络身份
        this.defaultIdentity = defaultIdentity
        // 对秘钥加密所使用的加密算法
        this.privateKeyEncryptionAlgorithm = privateKeyEncryptionAlgorithm
    }
	// // 一个map，存储了身份名字和网络身份实体的映射
	// identifies map[string]*identity.Identity
	// // 默认网络身份
	// defaultIdentity *identity.Identity
	// // 对秘钥加密所使用的加密算法
	// privateKeyEncryptionAlgorithm minsecurity.SymmetricAlgorithm

    //
    // 初始化
    //
    // @Description:
    //	1. 尝试从本地持久化存储中拉取所有的网络身份信息；
    //	2. 尝试获取本地持久化记录当中本系统的默认网络身份信息；
    // @receiver i *IdentifyManager
    // @return error
    //
    Init() {
        sqlite.OpenDefault()
        // 首先尝试从本地持久化存储中拉取所有的网络身份信息
        var err = this.loadAllIdentifies()
        if (err != null) {
            return err
        }

        // 获取默认的网络身份
        var defaultIdentity = persist.GetDefaultIdentityFromStorage("")
        if (defaultIdentity[1] != null) {
            return defaultIdentity[1]
        }
        if (defaultIdentity[0] != null || defaultIdentity[0] != undefined) {
            this.defaultIdentity = defaultIdentity[0]
        }

        // 默认使用 SM4_ECB 对秘钥进行加解密
        this.privateKeyEncryptionAlgorithm = com.SymmetricAlgorithm.SM4ECB
        return null
    }

    //
    // 从持久化载体中获取所有存储的网络身份信息，放到 identifies 当中
    //
    // @Description:
    // @receiver i *IdentifyManager
    // @return error
    //
    loadAllIdentifies() {
        // 从持久化存储中获取所有的网络身份
        var localIdentifies = persist.GetAllIdentityFromStorage("")
        if (localStorage[1] != null) {
            return localStorage[1]
        }
        // 将网络身份存储到Map当中
        var m = new Map()
        if (localIdentifies[0] === null){
            this.identifies = m
            return null
        }else {
            for (var i = 0; i < localIdentifies[0].length; i++) {
                m.set(localIdentifies[0][i].Name, localIdentifies[0][i])
            }
        }
        this.identifies = m
        return null
    }

    //
    // 通过身份的名字获取网络身份
    //
    // @Description:
    // @receiver i *IdentifyManager
    // @param name
    // @return *identity.Identity
    //
    GetIdentifyByName(name) {
        return this.identifies.get(name)
    }

    //
    // 通过网络身份的名字和对应的密码删除一个网络身份
    //
    // @Description:
    //	1. 如果这个网络身份只包含证书、公钥，并不包含私钥
    // @receiver i *identity.Identity
    // @param name string
    // @param password string
    // @return bool, error
    //
    DeleteIdentifyByName(name, password) {
        // 首先在Map里面删除它
        this.identifies.delete(name)
        return persist.DeleteIdentityByNameFromStorage(name, password)
    }

    //
    // 将一个网络身份保存到本地
    //
    // @Description:
    //	1. 首先检查网络身份在本地是否存在，如果存在，且 force = false，则不覆盖，保存失败
    //	2. 如果网络身份已存在，且 force = true，则覆盖原先的网络身份
    //	3. 如果网络身份不存在，则直接保存
    // @receiver i *IdentifyManager
    // @param newIdentity  *identity.Identity
    // @param force bool
    // @return error
    //
    SaveIdentify(newIdentity, force) {
        sqlite.OpenDefault()
        if (this.ExistIdentity(newIdentity.Name) && !force) {
            // 身份已存在，且不强制覆盖，则返回错误，保存失败
            return new IdentityManagerError(util.format("Save Identify failed, identify with name %s already exist!", newIdentity.Name))
        }

        // 身份不存在，或者存在但是指定强制覆盖，则保存该网络身份

        // 将新的网络身份进行持久化存储
        var err = persist.PersistIdentity(newIdentity, ""); 
        if (err[1] != null) {
            return err[1]
        }

        this.identifies.set(newIdentity.Name, newIdentity)
        return null
    }

    //
    // 指定一个身份名字在本地创建一个网络身份
    //
    // @Description:
    // @receiver i *IdentifyManager
    // @param name string
    // @param password string
    // @return *identity.Identity
    // @return error
    //
    CreateIdentityByName(name, password) {
        // 默认使用国密算法进行加解密和签名
        return this.CreateIdentityByNameAndKeyParam(name, new identity.KeyParam(
            com.PublicKeyAlgorithm.SM2,com.SignatureAlgorithm.SM2WithSM3), password)
    }

    //
    // 指定一个身份名字和KeyParam，在本地创建一个网络身份
    //
    // @Description:
    //	1. 首先检查指定的身份名字在本地存储中是否已经存在，如果已经存在则创建失败；
    //	2. 如果名字不冲突，则检查 password 是否为空字符串：
    //		a. 如果 password 为空字符串，则不对身份的秘钥进行加密保护；
    //		b. 如果 password 为非空字符串，则认为需要用其对秘钥进行加密保护，调用 Identify.Lock(password)
    // @receiver i *IdentifyManager 
    // @param name string
    // @param param identity.KeyParam
    // @param password string
    // @return *identity.Identity
    // @return error
    //
    CreateIdentityByNameAndKeyParam(name, param, password) {
        // 第一步，判断在本地是否有同名的网络身份
        if (this.identifies.get(name) != null) {
            return [null, new IdentityManagerError(util.format(
                "Identify name => %s, already exists!", name))]
        }

        // 本地生成一堆公私钥
        let keypair = sm2.GenKeyPair()
        let pri = keypair[0]
        let pub = keypair[1]
        // 实例化一个新的网络身份
        var newIdentity = new identity.Identity
        newIdentity.Name = name
        newIdentity.Prikey = new sec.PrivateKey(pri.PrivateKey)
        newIdentity.Pubkey = new sec.PublicKey(pub.PublicKey)
        newIdentity.KeyParam = param
        console.log("newIdentity is", newIdentity)

        // 如果 password 为非空字符串，则认为其需要对秘钥进行加密保护，使用传入的密码对秘钥进行锁定
        if (password != "") {
            let err = newIdentity.Lock(password, com.SymmetricAlgorithm.SM4ECB)
            if (err[1] != null) {
                return [null, err[1]]
            }
        }
        console.log("newIdentity is locked?", newIdentity)
        // 将新生成的网络身份进行持久化存储
        var err = persist.PersistIdentity(newIdentity, "")
        if (err[1] != null) {
            return [null, err[1]]
        }

        // 持久化存储成功则在内存map中也存储一份
        this.identifies.set(name, newIdentity)

        return [newIdentity, null]
    }

    //
    // 设置当前系统的默认网络身份
    //
    // @Description:
    // @receiver i *IdentifyManager
    // @param identity  *identity.Identity
    // @return bool, error
    //
    SetDefaultIdentity(identity) {
        if (identity === null) {
            return [false, new KeyChain.KeyChainError(util.format(
                "Not allow set nil as default Identity!"))]
        }
        this.defaultIdentity = identity
        return persist.SetDefaultIdentityByNameInStorage(identity.Name, identity.Passwd)
    }

    //
    // 判断某个网络身份是否存在
    //
    // @Description:
    // @receiver i
    // @param name  string
    // @return bool
    //
    ExistIdentity(name) {
        console.log("exist?", this.identifies.get(name))
        console.log("manager.identifies", this.identifies)
        return this.identifies.get(name) != null
    }
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////
///// 错误处理
/////////////////////////////////////////////////////////////////////////////////////////////////////////

class IdentityManagerError {
    constructor(msg){
        this.msg = msg
    }
	Error() {
        return util.format("IdentifyManagerError: %s", this.msg)
    }
}

module.exports = {
    IdentifyManager: IdentifyManager
}